<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>交互式音乐频谱可视化器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Avenir Next', 'Segoe UI', sans-serif;
        }

        body {
            background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
            color: #fff;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
            overflow-x: hidden;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
            padding: 20px;
            width: 100%;
            max-width: 900px;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 16px;
            backdrop-filter: blur(10px);
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        h1 {
            font-size: 2.8rem;
            margin-bottom: 10px;
            background: linear-gradient(90deg, #00c9ff, #92fe9d);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            text-shadow: 0 2px 10px rgba(0, 201, 255, 0.3);
        }

        .subtitle {
            font-size: 1.2rem;
            opacity: 0.9;
            margin-bottom: 15px;
        }

        .container {
            display: flex;
            flex-wrap: wrap;
            gap: 25px;
            justify-content: center;
            width: 100%;
            max-width: 1100px;
        }

        .visualizer-container {
            flex: 1;
            min-width: 300px;
            max-width: 650px;
            height: 400px;
            background: rgba(0, 0, 0, 0.2);
            border-radius: 16px;
            overflow: hidden;
            box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3);
            position: relative;
        }

        #visualizer {
            background: rgba(0, 0, 0, 0.7);
            width: 100%;
            height: 100%;
        }

        .controls {
            flex: 1;
            min-width: 300px;
            max-width: 400px;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 16px;
            padding: 25px;
            backdrop-filter: blur(10px);
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .control-group {
            margin-bottom: 25px;
            padding-bottom: 15px;
            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
        }

        h2 {
            font-size: 1.4rem;
            margin-bottom: 20px;
            color: #00c9ff;
            display: flex;
            align-items: center;
        }

        h2::before {
            content: "◉";
            margin-right: 10px;
            color: #92fe9d;
        }

        .slider-container {
            margin-bottom: 20px;
        }

        label {
            display: block;
            margin-bottom: 10px;
            font-weight: 500;
            color: #92fe9d;
        }

        input[type="range"] {
            width: 100%;
            height: 10px;
            -webkit-appearance: none;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 5px;
            outline: none;
        }

        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            background: #00c9ff;
            cursor: pointer;
            box-shadow: 0 0 15px rgba(0, 201, 255, 0.7);
            border: 2px solid #fff;
        }

        .value-display {
            display: inline-block;
            margin-left: 10px;
            background: rgba(146, 254, 157, 0.1);
            padding: 3px 12px;
            border-radius: 12px;
            min-width: 40px;
            text-align: center;
            font-weight: 600;
        }

        .buttons {
            display: flex;
            gap: 15px;
            flex-wrap: wrap;
        }

        button {
            padding: 14px 25px;
            border: none;
            border-radius: 50px;
            background: linear-gradient(90deg, #00c9ff, #92fe9d);
            color: #0f2027;
            font-weight: bold;
            cursor: pointer;
            transition: transform 0.2s, box-shadow 0.2s;
            flex: 1;
            min-width: 140px;
            font-size: 1rem;
            box-shadow: 0 5px 15px rgba(0, 201, 255, 0.4);
        }

        button:hover {
            transform: translateY(-3px);
            box-shadow: 0 8px 20px rgba(0, 201, 255, 0.6);
        }

        button:active {
            transform: translateY(0);
        }

        .preset-buttons {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 12px;
        }

        .preset-btn {
            background: linear-gradient(90deg, #ff6b6b, #ff8e53);
            padding: 10px;
            font-size: 0.9rem;
        }

        .visualization-types {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 12px;
        }

        .viz-type-btn {
            background: linear-gradient(90deg, #8e2de2, #4a00e0);
            padding: 12px;
        }

        .instructions {
            margin-top: 30px;
            background: rgba(0, 0, 0, 0.3);
            padding: 20px;
            border-radius: 16px;
            max-width: 900px;
            text-align: center;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .instructions p {
            margin-bottom: 10px;
            line-height: 1.6;
        }

        footer {
            margin-top: 40px;
            text-align: center;
            opacity: 0.7;
            font-size: 0.9rem;
        }

        @media (max-width: 768px) {
            .container {
                flex-direction: column;
                align-items: center;
            }

            .visualizer-container, .controls {
                max-width: 100%;
            }

            h1 {
                font-size: 2.2rem;
            }
        }

        /* 霓虹灯效果 */
        .neon {
            text-shadow: 0 0 5px #00c9ff, 0 0 10px #00c9ff, 0 0 15px #00c9ff, 0 0 20px #00c9ff;
        }

        /* 脉冲动画 */
        @keyframes pulse {
            0% { opacity: 0.7; }
            50% { opacity: 1; }
            100% { opacity: 0.7; }
        }

        .pulse {
            animation: pulse 2s infinite;
        }
    </style>
</head>
<body>
<header>
    <h1 class="neon">交互式音乐频谱可视化器</h1>
    <p class="subtitle">通过虚拟音频输入创建动态视觉效果</p>
    <p class="pulse">移动滑块和按钮来改变可视化效果</p>
</header>

<div class="container">
    <div class="visualizer-container">
        <canvas id="visualizer"></canvas>
    </div>

    <div class="controls">
        <div class="control-group">
            <h2>频谱设置</h2>

            <div class="slider-container">
                <label>频带数量: <span id="band-value" class="value-display">64</span></label>
                <input type="range" id="band-slider" min="16" max="128" value="64">
            </div>

            <div class="slider-container">
                <label>高度比例: <span id="height-value" class="value-display">80</span></label>
                <input type="range" id="height-slider" min="10" max="200" value="80">
            </div>

            <div class="slider-container">
                <label>平滑度: <span id="smooth-value" class="value-display">0.7</span></label>
                <input type="range" id="smooth-slider" min="0" max="100" value="70">
            </div>
        </div>

        <div class="control-group">
            <h2>颜色效果</h2>

            <div class="slider-container">
                <label>色调: <span id="hue-value" class="value-display">220</span></label>
                <input type="range" id="hue-slider" min="0" max="360" value="220">
            </div>

            <div class="slider-container">
                <label>饱和度: <span id="saturation-value" class="value-display">80</span></label>
                input type="range" id="saturation-slider" min="0" max="100" value="80">
            </div>

            <div class="buttons preset-buttons">
                <button class="preset-btn" data-preset="rainbow">彩虹模式</button>
                <button class="preset-btn" data-preset="ocean">海洋模式</button>
                <button class="preset-btn" data-preset="forest">森林模式</button>
                <button class="preset-btn" data-preset="sunset">日落模式</button>
            </div>
        </div>

        <div class="control-group">
            <h2>可视化模式</h2>
            <div class="buttons visualization-types">
                <button class="viz-type-btn" data-type="bars">条形图</button>
                <button class="viz-type-btn" data-type="circle">圆形图</button>
                <button class="viz-type-btn" data-type="wave">波形图</button>
                <button class="viz-type-btn" data-type="particles">粒子</button>
            </div>
        </div>

        <div class="control-group">
            <h2>音频控制</h2>
            <div class="buttons">
                <button id="play-btn">播放/暂停</button>
                <button id="randomize-btn">随机化</button>
            </div>
        </div>
    </div>
</div>

<div class="instructions">
    <p>这是一个模拟的音乐频谱可视化器，无需真实音频输入即可生成动态效果。</p>
    <p>尝试不同的可视化模式和颜色预设，创建您自己的独特视觉效果！</p>
</div>

<footer>
    <p>交互式音乐频谱可视化器 | 纯HTML、CSS和JavaScript实现</p>
</footer>

<script>
    document.addEventListener('DOMContentLoaded', function() {
        const canvas = document.getElementById('visualizer');
        const ctx = canvas.getContext('2d');

        // 设置Canvas尺寸
        function resizeCanvas() {
            canvas.width = canvas.offsetWidth;
            canvas.height = canvas.offsetHeight;
        }

        window.addEventListener('resize', resizeCanvas);
        resizeCanvas();

        // 可视化器状态
        const state = {
            playing: true,
            bands: 64,
            heightScale: 0.8,
            smoothing: 0.7,
            hue: 220,
            saturation: 80,
            visualizationType: 'bars',
            frequencyData: []
        };

        // 初始化频率数据
        function initFrequencyData() {
            state.frequencyData = [];
            for (let i = 0; i < state.bands; i++) {
                state.frequencyData.push({
                    value: Math.random() * 0.5,
                    target: Math.random() * 0.5,
                    speed: 0.02 + Math.random() * 0.03
                });
            }
        }

        initFrequencyData();

        // 获取UI元素
        const bandSlider = document.getElementById('band-slider');
        const heightSlider = document.getElementById('height-slider');
        const smoothSlider = document.getElementById('smooth-slider');
        const hueSlider = document.getElementById('hue-slider');
        const saturationSlider = document.getElementById('saturation-slider');

        const bandValue = document.getElementById('band-value');
        const heightValue = document.getElementById('height-value');
        const smoothValue = document.getElementById('smooth-value');
        const hueValue = document.getElementById('hue-value');
        const saturationValue = document.getElementById('saturation-value');

        const playBtn = document.getElementById('play-btn');
        const randomizeBtn = document.getElementById('randomize-btn');
        const presetButtons = document.querySelectorAll('.preset-btn');
        const vizTypeButtons = document.querySelectorAll('.viz-type-btn');

        // 更新UI显示
        bandSlider.addEventListener('input', () => {
            state.bands = parseInt(bandSlider.value);
            bandValue.textContent = state.bands;
            initFrequencyData();
        });

        heightSlider.addEventListener('input', () => {
            state.heightScale = parseInt(heightSlider.value) / 100;
            heightValue.textContent = parseInt(heightSlider.value);
        });

        smoothSlider.addEventListener('input', () => {
            state.smoothing = parseInt(smoothSlider.value) / 100;
            smoothValue.textContent = state.smoothing.toFixed(1);
        });

        hueSlider.addEventListener('input', () => {
            state.hue = parseInt(hueSlider.value);
            hueValue.textContent = state.hue;
        });

        saturationSlider.addEventListener('input', () => {
            state.saturation = parseInt(saturationSlider.value);
            saturationValue.textContent = state.saturation;
        });

        // 播放/暂停
        playBtn.addEventListener('click', () => {
            state.playing = !state.playing;
        });

        // 随机化
        randomizeBtn.addEventListener('click', () => {
            initFrequencyData();
            hueSlider.value = Math.floor(Math.random() * 360);
            state.hue = parseInt(hueSlider.value);
            hueValue.textContent = state.hue;
        });

        // 预设按钮
        presetButtons.forEach(btn => {
            btn.addEventListener('click', () => {
                const preset = btn.getAttribute('data-preset');

                switch(preset) {
                    case 'rainbow':
                        hueSlider.value = 0;
                        saturationSlider.value = 100;
                        break;
                    case 'ocean':
                        hueSlider.value = 220;
                        saturationSlider.value = 80;
                        break;
                    case 'forest':
                        hueSlider.value = 120;
                        saturationSlider.value = 80;
                        break;
                    case 'sunset':
                        hueSlider.value = 30;
                        saturationSlider.value = 100;
                        break;
                }

                state.hue = parseInt(hueSlider.value);
                state.saturation = parseInt(saturationSlider.value);
                hueValue.textContent = state.hue;
                saturationValue.textContent = state.saturation;
            });
        });

        // 可视化类型按钮
        vizTypeButtons.forEach(btn => {
            btn.addEventListener('click', () => {
                state.visualizationType = btn.getAttribute('data-type');

                vizTypeButtons.forEach(b => b.style.opacity = '0.7');
                btn.style.opacity = '1';
            });
        });

        // 更新频率数据
        function updateFrequencyData() {
            for (let i = 0; i < state.bands; i++) {
                const band = state.frequencyData[i];

                if (state.playing) {
                    // 随机变化目标值
                    if (Math.random() < 0.02) {
                        band.target = Math.random();
                    }

                    // 平滑过渡到目标值
                    band.value += (band.target - band.value) * band.speed;
                }

                // 应用平滑
                band.value = band.value * state.smoothing + band.target * (1 - state.smoothing);
            }
        }

        // 绘制条形图可视化
        function drawBars() {
            const width = canvas.width / state.bands;

            for (let i = 0; i < state.bands; i++) {
                const value = state.frequencyData[i].value;
                const height = value * canvas.height * state.heightScale;
                const x = i * width;
                const y = canvas.height - height;

                const hue = (state.hue + i * 360 / state.bands) % 360;
                ctx.fillStyle = `hsl(${hue}, ${state.saturation}%, 70%)`;

                ctx.fillRect(x, y, width - 2, height);

                // 添加光晕效果
                ctx.shadowBlur = 15;
                ctx.shadowColor = `hsl(${hue}, ${state.saturation}%, 70%)`;
            }
        }

        // 绘制圆形可视化
        function drawCircle() {
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            const maxRadius = Math.min(centerX, centerY) * 0.8;

            ctx.beginPath();

            for (let i = 0; i < state.bands; i++) {
                const value = state.frequencyData[i].value;
                const angle = (i / state.bands) * Math.PI * 2;
                const radius = value * maxRadius * state.heightScale + 20;

                const x = centerX + Math.cos(angle) * radius;
                const y = centerY + Math.sin(angle) * radius;

                if (i === 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }

                // 绘制点
                const hue = (state.hue + i * 360 / state.bands) % 360;
                ctx.fillStyle = `hsl(${hue}, ${state.saturation}%, 70%)`;
                ctx.fillRect(x - 2, y - 2, 4, 4);
            }

            ctx.closePath();
            ctx.strokeStyle = `hsl(${state.hue}, ${state.saturation}%, 70%)`;
            ctx.lineWidth = 2;
            ctx.stroke();
        }

        // 绘制波形可视化
        function drawWave() {
            const centerY = canvas.height / 2;
            const amplitude = canvas.height * 0.4 * state.heightScale;

            ctx.beginPath();

            for (let i = 0; i < state.bands; i++) {
                const value = state.frequencyData[i].value;
                const x = (i / state.bands) * canvas.width;
                const y = centerY + value * amplitude;

                if (i === 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }

            ctx.lineWidth = 3;
            const hue = state.hue;
            ctx.strokeStyle = `hsl(${hue}, ${state.saturation}%, 70%)`;
            ctx.stroke();

            // 填充波形
            ctx.lineTo(canvas.width, canvas.height);
            ctx.lineTo(0, canvas.height);
            ctx.closePath();
            ctx.fillStyle = `hsla(${hue}, ${state.saturation}%, 70%, 0.2)`;
            ctx.fill();
        }

        // 绘制粒子可视化
        function drawParticles() {
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;

            for (let i = 0; i < state.bands; i++) {
                const value = state.frequencyData[i].value;
                const angle = (i / state.bands) * Math.PI * 2;
                const distance = value * centerX * state.heightScale;

                const x = centerX + Math.cos(angle) * distance;
                const y = centerY + Math.sin(angle) * distance;

                const size = 2 + value * 8;
                const hue = (state.hue + i * 360 / state.bands) % 360;

                ctx.beginPath();
                ctx.arc(x, y, size, 0, Math.PI * 2);
                ctx.fillStyle = `hsl(${hue}, ${state.saturation}%, 70%)`;
                ctx.fill();

                // 添加光晕效果
                ctx.shadowBlur = 15;
                ctx.shadowColor = `hsl(${hue}, ${state.saturation}%, 70%)`;
            }
        }

        // 动画循环
        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            updateFrequencyData();

            // 根据选择的类型绘制可视化
            switch(state.visualizationType) {
                case 'bars':
                    drawBars();
                    break;
                case 'circle':
                    drawCircle();
                    break;
                case 'wave':
                    drawWave();
                    break;
                case 'particles':
                    drawParticles();
                    break;
            }

            // 重置阴影效果
            ctx.shadowBlur = 0;

            requestAnimationFrame(animate);
        }

        // 启动动画
        animate();
    });
</script>
</body>
</html>